package net.skycloud.f5.model;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import net.skycloud.f5.exception.ParamCheckFailedException;
import net.skycloud.f5.validation.ADD;
import net.skycloud.f5.validation.UPDATE;
import org.hibernate.annotations.Type;
import org.hibernate.validator.constraints.NotBlank;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.*;

/**
 * Created by ayang on 2017/8/1.
 */
@Entity
@Table(name = "NAP_DEVICE", indexes = {
        @Index(name = "IDX_DEVICE", columnList = "ID, NAME, VENDOR, TYPE")
})
@ApiModel(value = "Device", description = "设备API")
public class Device {
    @Id
    @Column(name = "ID")
    @Type(type = "org.hibernate.type.PostgresUUIDType")
    @ApiModelProperty(value = "设备ID", required = false, example = "876C2203-7472-44E8-9EB6-13CF372D326C")
    private UUID id;

    @Column(name = "NAME", length = 50)
    @NotBlank(groups = {ADD.class}, message = "error.not_blank")
    @Size(min = 1, max = 50, groups = {ADD.class, UPDATE.class}, message = "error.size")
    @ApiModelProperty(value = "设备名,长度1~50", required = true, example = "dev-1")
    private String name;

    @Column(name = "VENDOR", length = 50)
    @NotBlank(groups = {ADD.class}, message = "error.not_blank")
    @Size(min = 1, max = 50, groups = {ADD.class, UPDATE.class}, message = "error.size")
    @ApiModelProperty(value = "厂商,长度1~50", required = true, example = "cisco", allowableValues = "cisco")
    private String vendor;

    @Column(name = "TYPE", length = 50)
    @NotBlank(groups = {ADD.class}, message = "error.not_blank")
    @Size(min = 1, max = 50, groups = {ADD.class, UPDATE.class}, message = "error.size")
    @ApiModelProperty(value = "型号,长度1~50", required = true, example = "asav", allowableValues = "asav")
    private String type;

    @Column(name = "VERSION", length = 50)
    @NotBlank(groups = {ADD.class}, message = "error.not_blank")
    @Size(min = 1, max = 50, groups = {ADD.class, UPDATE.class}, message = "error.size")
    @ApiModelProperty(value = "版本,长度1~50", required = true, example = "9.6.2", allowableValues = "9.6.2")
    private String version;

    @Column(name = "DELETED")
    @NotNull
    @ApiModelProperty(hidden = true)
    private Boolean deleted;

    @Column(name = "CREATED_AT")
    @Temporal(TemporalType.TIMESTAMP)
    @NotNull
    @ApiModelProperty(value = "创建时间,timestamp", required = false)
    private Date createdAt;

    @Column(name = "UPDATED_AT")
    @Temporal(TemporalType.TIMESTAMP)
    @NotNull
    @ApiModelProperty(value = "更新时间,timestamp", required = false)
    private Date updatedAt;

    @Column(name = "PROPERTY", columnDefinition = "TEXT")
    @NotBlank(groups = {ADD.class}, message = "error.not_blank")
    @Size(min = 1, groups = {ADD.class, UPDATE.class}, message = "error.size")
    @ApiModelProperty(
            value = "设备属性",
            required = true,
            allowableValues = "protocol,mgmt_ip,read_cred_id,write_cred_id,healthcheck,sync,healthcheck_interval,sync_interval,mode,syslog")
    private String property;

    @Column(name = "MODE", length = 25)
    @Enumerated(EnumType.STRING)
    @ApiModelProperty(
            value = "设备属性",
            required = true, allowableValues = "READ_WRITE,READ_ONLY,DISABLED")
    private DeviceMode mode;

    @Column(name = "DESCRIPTION", length = 255)
    @Size(min = 0, max = 255, groups = {ADD.class, UPDATE.class}, message = "error.size")
    @ApiModelProperty(value = "详细说明,长度0~255", required = false, example = "description to add")
    private String description;

    @Column(name = "SYNCED_STATE", length = 25)
    @Enumerated(EnumType.STRING)
    @ApiModelProperty(
            value = "同步状态",
            required = true, allowableValues = "UN_SYNCED,SYNCING,SYNCED")
    private SyncedState synced;

    @Column(name = "PRE_SYNCED_STATE", length = 25)
    @Enumerated(EnumType.STRING)
    @ApiModelProperty(
            value = "上次同步状态,相当于备份同步状态",
            required = false, allowableValues = "UN_SYNCED,SYNCING,SYNCED")
    private SyncedState preSynced;

    @ApiModelProperty(
            value = "设备状态,运行中:RUNNING,宕机:DOWN,该属性不在数据库保存", example = "RUNNING")
    private DeviceState state;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "NAP_DEV_CRED_R",
            joinColumns = @JoinColumn(name = "DEV_ID", referencedColumnName = "ID"),
            inverseJoinColumns = @JoinColumn(name = "CRED_ID", referencedColumnName = "ID"))
    @ApiModelProperty(value = "设备连接凭证列表", name = "credentials", required = false)
    private Set<DeviceCredential> credentials;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "NAP_DEV_GROUP_R",
            joinColumns = @JoinColumn(name = "DEV_ID", referencedColumnName = "ID"),
            inverseJoinColumns = @JoinColumn(name = "GROUP_ID", referencedColumnName = "ID"))
    @ApiModelProperty(value = "设备组列表", name = "groups", required = false)
    private Set<DeviceGroup> groups;

    @Column(name = "managed", columnDefinition = "BOOLEAN")
    @ApiModelProperty(value = "设备纳管信息", name = "managed", required = true)
    private Boolean managed;

    @Column(name = "ext", columnDefinition = "TEXT")
    @ApiModelProperty(value = "设备扩展信息", name = "ext", required = false)
    private String ext;

    @Column(name = "ROUTER_TYPE", length = 25)
    @Enumerated(EnumType.STRING)
    @ApiModelProperty(
            value = "路由类型（路由/透明墙）",
            required = true, allowableValues = "ROUTER,TRANSPARENT,SWITCH_FIREWALL")
    private RouterType routerType;

    @Transient
    @ApiModelProperty(value = "设备扩展信息Json", name = "extJson", required = false)
    private Map<String, Object> extJson;

    public void fillStringFromJson() throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            if (this.extJson != null) {
                this.setExt(objectMapper.writeValueAsString(this.extJson));
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ParamCheckFailedException(String.format("write string failed, %s", e.getMessage()));
        }
    }
    public void fillJsonFromString() throws Exception {
         ObjectMapper objectMapper = new ObjectMapper();
        try {
            if (this.ext != null) {
                this.setExtJson(objectMapper.readValue(this.getExt(),
                        new TypeReference<HashMap<String, Object>>() {
                        }));
            } else {
                this.setExtJson(new HashMap<String, Object>());
            }
        } catch (Exception e) {
            throw new ParamCheckFailedException(String.format("read string to object failed, %s", e.getMessage()));
        }
    }
    public Device() {
    }

    private Device(Builder builder) {
        setId(builder.id);
        setName(builder.name);
        setVendor(builder.vendor);
        setType(builder.type);
        setVersion(builder.version);
        setDeleted(builder.deleted);
        setCreatedAt(builder.createdAt);
        setUpdatedAt(builder.updatedAt);
        setProperty(builder.property);
        setMode(builder.mode);
        setDescription(builder.description);
        setState(builder.state);
        setCredentials(builder.credentials);
        setGroups(builder.groups);
        setManaged(builder.managed);
        setExt(builder.ext);
        setExtJson(builder.extJson);
    }

    public boolean equals(Object object) {
        if (object == null) {
            return false;
        }
        if (object instanceof Device) {
            Device device = ((Device) object);
            if (this.id == null
                    || device.getId() == null) {
                return this == object;
            }
            return device.getId().equals(this.id);
        }
        return false;
    }

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVendor() {
        return vendor;
    }

    public void setVendor(String vendor) {
        this.vendor = vendor;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public Set<DeviceCredential> getCredentials() {
        return credentials;
    }

    public void setCredentials(Set<DeviceCredential> credentials) {
        this.credentials = credentials;
    }

    public Set<DeviceGroup> getGroups() {
        return groups;
    }

    public void setGroups(Set<DeviceGroup> groups) {
        this.groups = groups;
    }

    public Boolean getDeleted() {
        return deleted;
    }

    public void setDeleted(Boolean deleted) {
        this.deleted = deleted;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public DeviceState getState() {
        return state;
    }

    public void setState(DeviceState state) {
        this.state = state;
    }

    public SyncedState getPreSynced() {
        return preSynced;
    }

    public void setPreSynced(SyncedState preSynced) {
        this.preSynced = preSynced;
    }

    public DeviceMode getMode() {
        return mode;
    }

    public void setMode(DeviceMode mode) {
        this.mode = mode;
    }

    public String getExt() {
        return ext;
    }

    public void setExt(String ext) {
        this.ext = ext;
    }

    public Map<String, Object> getExtJson() {
        return extJson;
    }

    public void setExtJson(Map<String, Object> extJson) {
        this.extJson = extJson;
    }

    public Boolean getManaged() {
        return managed;
    }

    public void setManaged(Boolean managed) {
        this.managed = managed;
    }

    public RouterType getRouterType() {
        return routerType;
    }

    public void setRouterType(RouterType routerType) {
        this.routerType = routerType;
	}
    public SyncedState getSynced() {
        return synced;
    }

    public void setSynced(SyncedState synced) {
        this.synced = synced;
    }

    @Override
    public String toString() {
        return "Device{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", vendor='" + vendor + '\'' +
                ", type='" + type + '\'' +
                ", version='" + version + '\'' +
                ", deleted=" + deleted +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                ", property='" + property + '\'' +
                ", mode=" + mode +
                ", description='" + description + '\'' +
                ", synced=" + synced +
                ", preSynced=" + preSynced +
                ", state=" + state +
                ", credentials=" + credentials +
                ", groups=" + groups +
                ", managed=" + managed +
                ", ext='" + ext + '\'' +
                ", extJson=" + extJson +
                '}';
    }


    public static final class Builder {
        private UUID id;
        private String name;
        private String vendor;
        private String type;
        private String version;
        private Boolean deleted;
        private Date createdAt;
        private Date updatedAt;
        private String property;
        private DeviceMode mode;
        private String description;
        private DeviceState state;
        private Set<DeviceCredential> credentials;
        private Set<DeviceGroup> groups;
        private Boolean managed;
        private String ext;
        private Map<String, Object> extJson;

        public Builder() {
        }

        public Builder setId(UUID id) {
            this.id = id;
            return this;
        }

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public Builder setVendor(String vendor) {
            this.vendor = vendor;
            return this;
        }

        public Builder setType(String type) {
            this.type = type;
            return this;
        }

        public Builder setVersion(String version) {
            this.version = version;
            return this;
        }

        public Builder setDeleted(Boolean deleted) {
            this.deleted = deleted;
            return this;
        }

        public Builder setCreatedAt(Date createdAt) {
            this.createdAt = createdAt;
            return this;
        }

        public Builder setUpdatedAt(Date updatedAt) {
            this.updatedAt = updatedAt;
            return this;
        }

        public Builder setProperty(String property) {
            this.property = property;
            return this;
        }

        public Builder setMode(DeviceMode mode) {
            this.mode = mode;
            return this;
        }

        public Builder setDescription(String description) {
            this.description = description;
            return this;
        }

        public Builder setState(DeviceState state) {
            this.state = state;
            return this;
        }

        public Builder setCredentials(Set<DeviceCredential> credentials) {
            this.credentials = credentials;
            return this;
        }

        public Builder setGroups(Set<DeviceGroup> groups) {
            this.groups = groups;
            return this;
        }

        public Builder setManaged(Boolean managed) {
            this.managed = managed;
            return this;
        }

        public Builder setExt(String ext) {
            this.ext = ext;
            return this;
        }

        public Builder setExtJson(Map<String, Object> extJson) {
            this.extJson = extJson;
            return this;
        }

        public Device build() {
            return new Device(this);
        }
    }
}
